package com.ha7419x.dto;

import com.ha7419x.exception.BizException;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import com.ha7419x.common.Constant;
import org.springframework.util.CollectionUtils;

@Data
public class FlowNodeStructure extends FlowNodeDto{

    /**
     * 父节点List
     */
    private List<FlowNodeStructure> parentNodeList = new ArrayList<>();;

    /**
     * 子节点List
     */
    private List<FlowNodeStructure> childNodeList = new ArrayList<>();;

    /**
     * 节点验证
     * @param cameraAlgoAreaMap 相机算法区域MAP
     * @param nodeIdList        链路上的节点ID，防止无限循环
     * @param endNodeList       结束节点List
     */
    public void verification(Map<String, Map<String, List<String>>> cameraAlgoAreaMap
            , List<String> nodeIdList, List<FlowNodeStructure> endNodeList) throws BizException{
        // 验证相机算法是否存在
        // checkCameraAlgoArea(cameraAlgoAreaMap);
        // 根据节点类型验证
        if (Objects.equals(this.getNodeType(), Constant.BIG_NODE)) {
            checkBizNode();
        } else if (Objects.equals(this.getNodeType(), Constant.SMALL_NODE)) {
            checkSmallNode();
        } else {
            throw new BizException(String.format("节点【%s】类型【%s】错误", this.getNodeName(), this.getNodeType()));
        }
        // 验证是否存在循环调用
        if (nodeIdList.contains(this.getId())) {
            throw new BizException(String.format("节点【%s】存在循环引用", this.getNodeName()));
        }
        List<String> newNodeIdList = new ArrayList<>();
        newNodeIdList.add(this.getId());
        newNodeIdList.addAll(nodeIdList);
        // 验证子节点
        for (FlowNodeStructure node: this.childNodeList) {
            node.verification(cameraAlgoAreaMap, newNodeIdList, endNodeList);
        }
        // 记录结束节点
        if (CollectionUtils.isEmpty(this.childNodeList) && !endNodeList.contains(this)) {
            endNodeList.add(this);
        }
    }

    public void addParentNode(FlowNodeStructure structure) {
        if (structure == null) {
            return;
        }
        parentNodeList.add(structure);
    }

    public void addChildNode(FlowNodeStructure structure) {
        if (structure == null) {
            return;
        }
        childNodeList.add(structure);
    }

    /**
     * 验证相机算法是否存在,合规
     * @param cameraAlgoAreaMap
     * @throws BizException
     */
    private void checkCameraAlgoArea(Map<String, Map<String, List<String>>> cameraAlgoAreaMap) throws BizException{
        Map<String, List<String>> algoAreaMap = cameraAlgoAreaMap.get(this.getCameraId());
        if (CollectionUtils.isEmpty(algoAreaMap)) {
            throw new BizException(String.format("节点【%s】相机不可用", this.getNodeName()));
        }
        List<String> areaList = algoAreaMap.get(this.getAlgoMark());
        if (CollectionUtils.isEmpty(areaList)) {
            throw new BizException(String.format("节点【%s】算法无区域", this.getNodeName()));
        }
    }

    /**
     * 验证大节点是否合规
     */
    private void checkBizNode() throws BizException{
        if (this.parentNodeList.size() == 0) {
            // 开始节点
            return;
        }
        if (this.parentNodeList.size() == 1) {
            if (!Objects.equals(Constant.BIG_NODE, this.parentNodeList.get(0).getNodeType())) {
                throw new BizException(String.format("节点【%s】父节点是一个时必须是大节点", this.getNodeName()));
            }
        } else {
            List<FlowNodeStructure> parentBigNodeList = this.parentNodeList.stream()
                    .filter(x -> Objects.equals(x.getNodeType(), Constant.BIG_NODE)).collect(Collectors.toList());
            if (parentBigNodeList.size() > 0) {
                throw new BizException(String.format("节点【%s】父节点是多个时必须都是小节点", this.getNodeName()));
            }
        }
    }

    /**
     * 验证小节点是合规
     * @throws BizException
     */
    private void checkSmallNode() throws BizException{
        if (this.parentNodeList.size() != 1) {
            throw new BizException(String.format("节点【%s】为小节点,父节点必须是一个", this.getNodeName()));
        }
        if (this.childNodeList.size() != 1) {
            throw new BizException(String.format("节点【%s】为小节点,子节点必须是一个", this.getNodeName()));
        }
        // 夫节点所有的子节点都要和当前节点的类型一致
        List<FlowNodeStructure> otherNodeTypeList = this.parentNodeList.get(0).childNodeList.stream()
                .filter(x -> !Objects.equals(x.getNodeType(), this.getNodeType())).collect(Collectors.toList());
        if (otherNodeTypeList.size() > 0) {
            throw new BizException(String.format("节点【%s】为小节点,其他同级小节点存在不同类型节点", this.getNodeName()));
        }
    }

    public FlowNodeStructure(String nodeName, String nodeType, String id, String pid) {
        this.setNodeName(nodeName);
        this.setNodeType(nodeType);
        this.setId(id);
        this.setPid(pid);
    }

    public FlowNodeStructure(String nodeName, String nodeType, String id) {
        this.setNodeName(nodeName);
        this.setNodeType(nodeType);
        this.setId(id);
    }

}
